﻿using System;
using System.Linq.Expressions;

namespace Ns
{
    #region Copyright
    //"Miscellaneous Utility Library" Software Licence
    //Version 1.0

    //Copyright (c) 2004-2008 Jon Skeet and Marc Gravell.
    //All rights reserved.

    //Redistribution and use in source and binary forms, with or without
    //modification, are permitted provided that the following conditions
    //are met:

    //1. Redistributions of source code must retain the above copyright
    //notice, this list of conditions and the following disclaimer.

    //2. Redistributions in binary form must reproduce the above copyright
    //notice, this list of conditions and the following disclaimer in the
    //documentation and/or other materials provided with the distribution.

    //3. The end-user documentation included with the redistribution, if
    //any, must include the following acknowledgment:

    //"This product includes software developed by Jon Skeet
    //and Marc Gravell. Contact skeet@pobox.com, or see 
    //http://www.pobox.com/~skeet/)."

    //Alternately, this acknowledgment may appear in the software itself,
    //if and wherever such third-party acknowledgments normally appear.

    //4. The name "Miscellaneous Utility Library" must not be used to endorse 
    //or promote products derived from this software without prior written 
    //permission. For written permission, please contact skeet@pobox.com.

    //5. Products derived from this software may not be called 
    //"Miscellaneous Utility Library", nor may "Miscellaneous Utility Library"
    //appear in their name, without prior written permission of Jon Skeet.

    //THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
    //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    //MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
    //IN NO EVENT SHALL JON SKEET BE LIABLE FOR ANY DIRECT, INDIRECT,
    //INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
    //BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    //CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    //LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
    //ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    //POSSIBILITY OF SUCH DAMAGE. 
    
    #endregion

    /// <summary>
    /// Provides standard operators over a single type
    /// Copyright (c) 2004-2008 Jon Skeet and Marc Gravell. All rights reserved.
    /// </summary>
    /// <seealso href="http://www.pobox.com/~skeet/">Miscellaneous Utility Library</seealso>
    internal static class Operator<T>
    {
        private static readonly Func<T, T, T> add;
        /// <summary>
        /// Returns a delegate to evaluate binary addition (+) for the given type; this delegate will throw
        /// an InvalidOperationException if the type T does not provide this operator, or for
        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
        /// </summary>
        public static Func<T, T, T> Add { get { return add; } }

        private static readonly Func<T, T, T> subtract;
        /// <summary>
        /// Returns a delegate to evaluate binary subtraction (-) for the given type; this delegate will throw
        /// an InvalidOperationException if the type T does not provide this operator, or for
        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
        /// </summary>
        public static Func<T, T, T> Subtract { get { return subtract; } }

        private static readonly Func<T, T, T> multiply;
        /// <summary>
        /// Returns a delegate to evaluate binary multiplication (*) for the given type; this delegate will throw
        /// an InvalidOperationException if the type T does not provide this operator, or for
        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
        /// </summary>
        public static Func<T, T, T> Multiply { get { return multiply; } }

        private static readonly Func<T, T, T> divide;

        /// <summary>
        /// Returns a delegate to evaluate binary division (/) for the given type; this delegate will throw
        /// an InvalidOperationException if the type T does not provide this operator, or for
        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
        /// </summary>
        public static Func<T, T, T> Divide { get { return divide; } }

        private static readonly Func<T, T, bool> equal;
        /// <summary>
        /// Returns a delegate to evaluate binary equality (==) for the given type; this delegate will throw
        /// an InvalidOperationException if the type T does not provide this operator, or for
        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
        /// </summary>
        public static Func<T, T, bool> Equal { get { return equal; } }

        private static readonly Func<T, T, bool> notEqual;
        /// <summary>
        /// Returns a delegate to evaluate binary inequality (!=) for the given type; this delegate will throw
        /// an InvalidOperationException if the type T does not provide this operator, or for
        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
        /// </summary>
        public static Func<T, T, bool> NotEqual { get { return notEqual; } }

        private static readonly Func<T, T, bool> greaterThan;
        /// <summary>
        /// Returns a delegate to evaluate binary greater-then (&gt;) for the given type; this delegate will throw
        /// an InvalidOperationException if the type T does not provide this operator, or for
        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
        /// </summary>
        public static Func<T, T, bool> GreaterThan { get { return greaterThan; } }

        private static readonly Func<T, T, bool> lessThan;
        /// <summary>
        /// Returns a delegate to evaluate binary less-than (&lt;) for the given type; this delegate will throw
        /// an InvalidOperationException if the type T does not provide this operator, or for
        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
        /// </summary>
        public static Func<T, T, bool> LessThan { get { return lessThan; } }

        private static readonly Func<T, T, bool> greaterThanOrEqual;
        /// <summary>
        /// Returns a delegate to evaluate binary greater-than-or-equal (&gt;=) for the given type; this delegate will throw
        /// an InvalidOperationException if the type T does not provide this operator, or for
        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
        /// </summary>
        public static Func<T, T, bool> GreaterThanOrEqual { get { return greaterThanOrEqual; } }

        private static readonly Func<T, T, bool> lessThanOrEqual;
        /// <summary>
        /// Returns a delegate to evaluate binary less-than-or-equal (&lt;=) for the given type; this delegate will throw
        /// an InvalidOperationException if the type T does not provide this operator, or for
        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
        /// </summary>
        public static Func<T, T, bool> LessThanOrEqual { get { return lessThanOrEqual; } }

        private static readonly Func<T, T> negate;
        /// <summary>
        /// Returns a delegate to evaluate unary negation (-) for the given type; this delegate will throw
        /// an InvalidOperationException if the type T does not provide this operator, or for
        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
        /// </summary>
        public static Func<T, T> Negate { get { return negate; } }
        
        private static readonly Func<T, T> not;        
        /// <summary>
        /// Returns a delegate to evaluate bitwise not (~) for the given type; this delegate will throw
        /// an InvalidOperationException if the type T does not provide this operator, or for
        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
        /// </summary>
        public static Func<T, T> Not { get { return not; } }

        private static readonly Func<T, T, T> or;
        /// <summary>
        /// Returns a delegate to evaluate bitwise or (|) for the given type; this delegate will throw
        /// an InvalidOperationException if the type T does not provide this operator, or for
        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
        /// </summary>
        public static Func<T, T, T> Or { get { return or; } }

        private static readonly Func<T, T, T> and;
        /// <summary>
        /// Returns a delegate to evaluate bitwise and (&amp;) for the given type; this delegate will throw
        /// an InvalidOperationException if the type T does not provide this operator, or for
        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
        /// </summary>
        public static Func<T, T, T> And { get { return and; } }

        private static readonly Func<T, T, T> xor;
        /// <summary>
        /// Returns a delegate to evaluate bitwise xor (^) for the given type; this delegate will throw
        /// an InvalidOperationException if the type T does not provide this operator, or for
        /// Nullable&lt;TInner&gt; if TInner does not provide this operator.
        /// </summary>
        public static Func<T, T, T> Xor { get { return xor; } }

        static Operator()
        {
            add = CreateExpression<T, T, T>(Expression.Add);
            subtract = CreateExpression<T, T, T>(Expression.Subtract);
            divide = CreateExpression<T, T, T>(Expression.Divide);
            multiply = CreateExpression<T, T, T>(Expression.Multiply);

            greaterThan = CreateExpression<T, T, bool>(Expression.GreaterThan);
            greaterThanOrEqual = CreateExpression<T, T, bool>(Expression.GreaterThanOrEqual);
            lessThan = CreateExpression<T, T, bool>(Expression.LessThan);
            lessThanOrEqual = CreateExpression<T, T, bool>(Expression.LessThanOrEqual);
            equal = CreateExpression<T, T, bool>(Expression.Equal);
            notEqual = CreateExpression<T, T, bool>(Expression.NotEqual);

            negate = CreateExpression<T, T>(Expression.Negate);
            and = CreateExpression<T, T, T>(Expression.And);
            or = CreateExpression<T, T, T>(Expression.Or);
            not = CreateExpression<T, T>(Expression.Not);
            xor = CreateExpression<T, T, T>(Expression.ExclusiveOr);
        }

        /// <summary>
        /// Create a function delegate representing a unary operation
        /// </summary>
        /// <typeparam name="T1">The parameter type</typeparam>
        /// <typeparam name="TResult">The return type</typeparam>
        /// <param name="body">Body factory</param>
        /// <returns>Compiled function delegate</returns>
        private static Func<T1, TResult> CreateExpression<T1, TResult>(Func<Expression, UnaryExpression> body)
        {
            var arg = Expression.Parameter(typeof(T), "arg");
            try
            {
                return Expression.Lambda<Func<T1, TResult>>(body(arg), arg).Compile();
            }
            catch (Exception ex)
            {
                return delegate { throw ex; };
            }
        }

        /// <summary>
        /// Create a function delegate representing a binary operation
        /// </summary>
        /// <typeparam name="T1">The first parameter type</typeparam>
        /// <typeparam name="T2">The second parameter type</typeparam>
        /// <typeparam name="TResult">The return type</typeparam>
        /// <param name="body">Body factory</param>
        /// <returns>Compiled function delegate</returns>
        private static Func<T1, T2, TResult> CreateExpression<T1, T2, TResult>(Func<Expression, Expression, BinaryExpression> body)
        {
            var arg1 = Expression.Parameter(typeof(T1), "arg1");
            var arg2 = Expression.Parameter(typeof(T2), "arg2");

            try
            {
                return Expression.Lambda<Func<T1, T2, TResult>>(body(arg1, arg2), arg1, arg2).Compile();
            }
            catch (Exception ex)
            {
                return delegate { throw ex; };
            }
        }
    }
}